/*
 * msgq_utils.c
 *
 *  Created on: 15/11/2012
 *      Author: julian
 */

#include <stdio.h>
#include <errno.h>
#include <sys/msg.h>
#include <sys/ipc.h>
#include <sys/time.h>
#include <signal.h>
#include <time.h>

#include "msgq_utils.h"

static sig_atomic_t timeout = 0;
static timer_t rcv_timer;


//--- timeout signal handler

static void timeout_handler(union sigval val) {
    timeout = 1;
    //signal(signum, SIG_DFL);
}

int msgq_timer_create () {

    struct sigevent evp;
    memset(&evp, 0, sizeof(struct sigevent));
    evp.sigev_notify = SIGEV_SIGNAL;
    evp.sigev_signo = SIGUSR1;
    evp.sigev_notify_function = timeout_handler;

    if (timer_create(CLOCK_REALTIME, &evp, &rcv_timer)==-1) {
        perror("xbee_init:timer_create");
        return -1;
    }
    return (0);
}

int msgq_timer_delete() {

    if (timer_delete(rcv_timer)==-1) {
        return (-1);
    }
    return (0);

}

int msgq_timer_create2 (timer_t *timer) {

    struct sigevent evp;
    memset(&evp, 0, sizeof(struct sigevent));
    evp.sigev_notify = SIGEV_SIGNAL;
    evp.sigev_signo = SIGUSR1;
    evp.sigev_notify_function = timeout_handler;

    if (timer_create(CLOCK_REALTIME, &evp, timer)==-1) {
        perror("msgq_timer_create2:timer_create");
        return -1;
    }
    return (0);
}

int msgq_timer_delete2(timer_t timer) {

    if (timer_delete(timer)==-1) {
        perror("msgq_timer_create2:timer_delete");
        return (-1);
    }
    return (0);

}

//--------------common message queue operations
int msgq_timed_recv(int msqid, void *qbuf, unsigned int size, unsigned long mtype, unsigned int timeout_msec, unsigned int timeout_sec) {

    if (timeout_msec<0 || timeout_msec>999) {
        fprintf(stderr, "Error xbee_receive:invalid timeout_msec(0<t<=999)\n");
        return -1;
    }
    struct itimerspec timing;
    timing.it_value.tv_sec = timeout_sec;
    timing.it_value.tv_nsec = timeout_msec * 1000000;
    timing.it_interval.tv_sec = 0;
    timing.it_interval.tv_nsec = 0;

//    struct itimerspec stop;
//    stop.it_value.tv_sec=0;

    timer_t timer;

    if (msgq_timer_create2(&timer)==-1) {
        perror("msgq_timed_recv.timer_create");
        return -1;
    }

    if (timer_settime(timer, 0, &timing, 0)==-1) {
        perror("msgq_timed_recv.timer_settime");
        //timer_delete(rcv_timer);
        return -1;
    }

    struct sigaction psa, old;
    memset (&psa, 0, sizeof (psa));
    psa.sa_handler = timeout_handler;
    sigemptyset(&psa.sa_mask);
    //psa.sa_flags = SA_SIGINFO;
    sigaction(SIGUSR1, &psa, &old);

    timeout = 0;
    while (!timeout) {
        if (msgrcv(msqid, (struct msgbuf *) qbuf,size - sizeof(long), mtype, MSG_NOERROR) == -1) {
            if (errno != EINTR) {
                fprintf(stderr, "Error %d msgq_timed_recv: %s\n", errno,
                        strerror(errno));
                sigaction(SIGUSR1, &old, NULL);
                return -1;
            }
        } else {
            timing.it_value.tv_sec=0;
            timing.it_value.tv_nsec=0;
            timer_settime(timer, 0, &timing, 0);
            msgq_timer_delete2(timer);
            //sigaction(SIGUSR1, &old, NULL);
            return 0;
        }
    }
    timing.it_value.tv_sec=0;
    timing.it_value.tv_nsec=0;
    timer_settime(timer, 0, &timing, 0);
    msgq_timer_delete2(timer);
    sigaction(SIGUSR1, &old, NULL);
    return -1; //timeout
}


int msgq_receive(int msqid, void *buf, unsigned long type, unsigned int size, int flags) {
    if (msgrcv(msqid, (struct msgbuf *)buf, size - sizeof(long), type, flags)== -1) {
        if ((flags & IPC_NOWAIT) && errno!=ENOMSG){
            perror("msgq_receive:");
            return -1;
        }
        else {
            perror("msgq_receive:");
            return -1;
        }
    }
    return 0;
}

int msgq_send(int msqid, void * buf, unsigned int size, int flags ) {
    if (msgsnd(msqid, buf,size - sizeof(long), 0)== -1) {
        perror("msgq_send");
        return -1;
    }
    return 0;
}

int msgq_create(key_t key) {
    int msqid = msgget(key, 0644 | IPC_CREAT);
    if (msqid == -1) {
        perror("msgq_create");
        return -1;
    }
    return msqid;
}

int msgq_link(key_t key){
    int msqid;
    if ((msqid=msgget(key, 0)) == -1) {
        fprintf(stderr, "msgq_link:Error %d al enlazar con la cola: %s\n",
                errno, strerror(errno));
        return -1;
    }
    return msqid;
}

int msgq_destroy(int msqid){
    if (msgctl(msqid, IPC_RMID, NULL) == -1) {
        fprintf(stderr, "msgq_destroy:Error %d al destruir la cola: %s\n", errno,strerror(errno));
        return -1;
    }
    return 0;
}


